package biolabs.gui;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.concurrent.ExecutionException;
import java.util.regex.Pattern;

import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.filechooser.FileFilter;

/**
 * InputComponent is the part of GUI that handles the script input.
 *
 * @author Tomas Mikula
 */
class InputComponent implements UIComponent {

	private static class ScalaFilter extends FileFilter {

		private static final Pattern SCALA_FILENAMES = Pattern.compile(".*\\.(?i:scala)");

		@Override
		public boolean accept(File f) {
			if(f.isDirectory())
				return true;
			return SCALA_FILENAMES.matcher(f.getName()).matches();
		}

		@Override
		public String getDescription() {
			return "Scala scripts";
		}

	}

	private static final Charset UTF8 = Charset.forName("UTF-8");

	private final JComponent content;
	private final JTextArea textArea;
	private final JFileChooser chooser;
	private final Loader loader;
	private final InputHandler handler;

	public InputComponent(InputHandler h) {

		handler = h;

		// create subcomponents
		textArea = new JTextArea();
		chooser = new JFileChooser();
		chooser.setFileFilter(new ScalaFilter());
		JButton loadBtn = new JButton("Open script");
		loader = new Loader();
		JButton evalBtn = new JButton("Evaluate");
		evalBtn.setToolTipText("Evaluate (Ctrl+Enter)");

		// lay out
		content = Box.createVerticalBox();
		JScrollPane scrollPane = new JScrollPane(textArea);
		scrollPane.setMaximumSize(Stage.UNLIMITED_SIZE);
		JPanel panel = new JPanel();
		panel.add(loadBtn);
		panel.add(evalBtn);
		panel.add(loader);
		content.add(scrollPane);
		content.add(panel);

		// set listeners
		textArea.addKeyListener(new CtrlEnterListener());
		evalBtn.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent evt) {
				evaluate();
			}
		});
		loadBtn.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent evt) {
				openScript();
			}
		});
	}

	@Override
	public Component getContent() {
		return content;
	}

	private void evaluate() {

		assert SwingUtilities.isEventDispatchThread();
		loader.on();
		final String input = textArea.getText();

		new SwingWorker<Object, Object>() {

			@Override
			protected Object doInBackground() {
				handler.eval(input);
				return null;
			}

			@Override
			protected void done() {
				try {
					get();
					textArea.setText(null);
				} catch (InterruptedException e) {
					throw new AssertionError();
				} catch (ExecutionException e) {
					// do nothing, was handled by handler
				}
				loader.off();
			}

		}.execute();
	}

	private void openScript() {
		int res = chooser.showOpenDialog(content);
		if(res != JFileChooser.APPROVE_OPTION)
			return;

		textArea.setText(null);
		File f = chooser.getSelectedFile();
		try {
			Reader r = new InputStreamReader(new FileInputStream(f), UTF8);
			int n;
			char[] cbuf = new char[1024];
			while((n=r.read(cbuf)) != -1)
				textArea.append(new String(cbuf, 0, n));
		} catch (FileNotFoundException e) {
			JOptionPane.showMessageDialog(content, e.getMessage(), "File not found", JOptionPane.ERROR_MESSAGE);
		} catch (IOException e) {
			JOptionPane.showMessageDialog(content, e.getMessage(), "File read error", JOptionPane.ERROR_MESSAGE);
		}
	}


	public static interface InputHandler {
		void eval(String input);
	}

	public class CtrlEnterListener implements KeyListener {

		@Override
		public void keyPressed(KeyEvent evt) {
			if(evt.isControlDown() && evt.getKeyCode() == KeyEvent.VK_ENTER)
				evaluate();
		}

		@Override
		public void keyReleased(KeyEvent evt) {
			// do nothing
		}

		@Override
		public void keyTyped(KeyEvent evt) {
			// do nothing
		}

	}

}
